using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;

using System.Drawing.Imaging;

namespace ClientGUI
{
    /// <summary>
    /// If something is ITextable, it can be converted to a text
    /// representation which can be converted back.
    /// </summary>
	interface ITextAble
	{
		/// <summary>
		/// Whether or not all of your children are ITextAble
		/// </summary>
		bool Pure { get; }

		/// <summary>
		/// The text you and your children represent.
		/// </summary>
		String Text { get;}
	}

    /// <summary>
    /// If somethin is IWordAble it can be broken into gdi drawable words.
    /// </summary>
	interface IWordAble
	{
		Word this[int index] { get; }
		int Length { get; }

	}
	
    /// <summary>
    /// Represents a single word, and aids in drawing it.
    /// </summary>
	class Word
	{
		String my_word;
		int my_width;
		int my_height;
		Font my_font;

        bool calculated = false;

        private static Dictionary<String, int> word_lengths = 
            new Dictionary<string, int>();


		public Word(String word, bool bold):
			this(word)
		{
			if (bold) {
				my_font = new Font("Sans Serif", 12.0f, FontStyle.Bold);
			}
		}

		public Word(String word)
		{
			my_word = word;
			my_font = new Font("Sans Serif", 12.0f);
			
			my_width =(int)(((float)word.Length) * my_font.Size);
			my_height = (int)my_font.GetHeight();
			//textextent();
		}

        /// <summary>
        /// Attempting to figure out the correct width by looking at the
        /// widest pixel which is drawn when it's drawn.
        /// </summary>
		private void textextent()
		{
            int tmp;
            if (word_lengths.TryGetValue(my_word, out tmp))
            {
                my_width = tmp;
                return;
            }
            Bitmap bitmap = new Bitmap((int)Width + 1, (int)Height + 1);
			Graphics g = Graphics.FromImage(bitmap);
			g.Clear(Color.White);
			g.DrawString(my_word, my_font, Brushes.Black, 
				new PointF(0, 0));


			int width = (int)my_width;

            unsafe
            {
                BitmapData dat = bitmap.LockBits(new Rectangle(
                    0, 0, Width, Height),
                    ImageLockMode.ReadOnly,
                    PixelFormat.Format32bppArgb);

                byte* data = (byte*)dat.Scan0;
                for (int x = (dat.Width-1)*4; x > 0; x-=4)
                {
                    for (int y = 0; y < dat.Height; ++y)
                    {
                        int val = *(data + (y*dat.Stride) + x);


                        if (val != 255)
                        {
                            width = Math.Min(width, x/4);
                            x = -1;
                            break;
                        }
                    }
                }
                bitmap.UnlockBits(dat);

/*                for (int x = bitmap.Width - 1; x > 0; --x)
                {
                    for (int y = 0; y < bitmap.Height; ++y)
                    {

                        if (!(bitmap.GetPixel(x, y).ToArgb() == -1))
                        {
                            width = Math.Min(width, x);
                            x = -1;
                            break;
                        }
                    } //this is the safe equivalent of this unsafe code..
                }*/
            }

			if (width < 0)
				width = (int)my_width;
			bitmap.Dispose();
            

			my_width = width+2;
            word_lengths[my_word] = my_width;
		}

        public int Width
        {
            get
            {
                if (!calculated)
                {
                    calculated = true;
                    textextent();
                }
                return my_width;
            }
        }
		public int Height { get { return my_height; } }

		public String Text { get { return my_word; } }

		public void Draw(Graphics g, int x, int y)
		{
			if (Text != "")
				g.DrawString(my_word, my_font, Brushes.Black, new PointF(x, y));
		}
	}
}
